package org.dromara.common.mybatis.interceptor;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;
import com.baomidou.mybatisplus.core.toolkit.ExceptionUtils;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.extension.parser.JsqlParserGlobal;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.*;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.expression.operators.relational.ExistsExpression;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.Statements;
import net.sf.jsqlparser.statement.delete.Delete;
import net.sf.jsqlparser.statement.insert.Insert;
import net.sf.jsqlparser.statement.select.FromItem;
import net.sf.jsqlparser.statement.select.Join;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.update.Update;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.dromara.common.core.utils.ObjectUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.properties.MpProperties;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author HansWu
 * @version 1.0
 * @description: 逻辑删除适配器, 让xml中sql操作也支持逻辑删除
 * @date 2025/5/19 16:07
 */
@Slf4j
public class DeleteMpXmlInterceptor implements InnerInterceptor {
    public static final String LOGIC_DELETE = "LOGIC_DELETE";
    public static final List<String> excludeFunctions = new ArrayList<>();
    protected String logicDeleteField;
    protected String logicDeleteValue;
    protected String logicNotDeleteValue;
    protected Map<String, Integer> logicDeleteTableIgnoreMap = new HashMap<>();

    public DeleteMpXmlInterceptor(MpProperties mpProperties) {
        this.logicDeleteField = mpProperties.getLogicDeleteField();
        this.logicDeleteValue = mpProperties.getLogicDeleteValue();
        this.logicNotDeleteValue = mpProperties.getLogicNotDeleteValue();
        if (CollectionUtil.isNotEmpty(mpProperties.getLogicDeleteExcludes())) {
            for (String logicDeleteTableIgnore : mpProperties.getLogicDeleteExcludes()) {
                logicDeleteTableIgnoreMap.put(logicDeleteTableIgnore, 1);
            }
        }
    }

    static {
        excludeFunctions.add("selectList");
        excludeFunctions.add("selectById");
        excludeFunctions.add("selectByIds");
        excludeFunctions.add("selectBatchIds");
        excludeFunctions.add("selectByMap");
        excludeFunctions.add("selectOne");
        excludeFunctions.add("selectCount");
        excludeFunctions.add("selectObjs");
        excludeFunctions.add("selectPage");
        excludeFunctions.add("selectMapsPage");
    }

    @SneakyThrows
    @Override
    public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {
        // do nothing
        if (StringUtils.isBlank(logicDeleteField)) {
            // 没有设置逻辑删除的字段名，也忽略添加逻辑删除
            return;
        }
        // 该函数用于获取表名对应的实体类
//        TableInfoHelper.getTableInfo(tableName);
        PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh);
        MappedStatement ms = mpSh.mappedStatement();
        SqlCommandType sct = ms.getSqlCommandType();
        if (sct != SqlCommandType.SELECT) {
            return;
        }
        String id = ms.getId();
        int lastIndexOf = id.lastIndexOf(".");
        String functionName = id.substring(lastIndexOf + 1);
        if (excludeFunctions.contains(functionName)) {
            // QueryWrapper的查询，本来就会加逻辑删除，不需要再加了
            return;
        }
        BoundSql boundSql = sh.getBoundSql();
        Statement statement = parseStatement(boundSql);

        Select select = (Select) statement;
        Select plainSelect = select.getPlainSelect();
        // 处理核心业务
        handleSelectBody(plainSelect);

        // 将处理完的数据重新转为MPBoundSql
        PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql();
        String sqlChange = statement.toString();
        mpBs.sql(sqlChange);
    }


    @SneakyThrows
    @Override
    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        if (InterceptorIgnoreHelper.willIgnoreOthersByKey(ms.getId(), LOGIC_DELETE)) {
            return;
        }

        if (StringUtils.isBlank(logicDeleteField)) {
            // 没有设置逻辑删除的字段名，也忽略添加逻辑删除
            return;
        }
        PluginUtils.MPBoundSql mpBs = PluginUtils.mpBoundSql(boundSql);
        Statement statement = parseStatement(boundSql);
        if (!(statement instanceof Select)) {
            return;
        }

        String id = ms.getId();
        int lastIndexOf = id.lastIndexOf(".");
        String functionName = id.substring(lastIndexOf + 1);
        if (excludeFunctions.contains(functionName)) {
            // QueryWrapper的查询，本来就会加逻辑删除，不需要再加了
            return;
        }

        Select select = (Select) statement;
        Select selectBody = select.getSelectBody();

        // 处理核心业务
        handleSelectBody(selectBody);

        // 将处理完的数据重新转为MPBoundSql
        String sqlChange = statement.toString();
        mpBs.sql(sqlChange);
    }

    protected Statement parseStatement(BoundSql boundSql) throws JSQLParserException {
        PluginUtils.MPBoundSql mpBs = PluginUtils.mpBoundSql(boundSql);
        String sql = mpBs.sql();
        return CCJSqlParserUtil.parse(sql);
    }

    protected void handleSelectBody(Select selectBody) {
        if (selectBody instanceof PlainSelect plainSelect) {

            // 处理join中的内容
            handleJoins(plainSelect);

            Expression where = plainSelect.getWhere();

            FromItem fromItem = plainSelect.getFromItem();
            EqualsTo equalsTo = getEqualTo(fromItem);
            if (ObjectUtils.isNotNull(equalsTo))
                if (where == null) {
                    // where条件为空，增加一个where，且赋值为 表名.del = 0
                    plainSelect.setWhere(equalsTo);
                } else {
                    // 普通where，后面直接加上本表的 表名.del = 0
                    AndExpression andExpression = new AndExpression(where, equalsTo);
                    plainSelect.setWhere(andExpression);

                    // 有子查询的处理子查询
                    handleWhereSubSelect(where);
                }
        }
    }

    /**
     * 这一段来自MybatisPlus的租户插件源码，通过递归的方式加上需要的SQL
     *
     * @param where
     */
    protected void handleWhereSubSelect(Expression where) {
        if (where == null) {
            return;
        }
        if (where instanceof FromItem) {
            processOtherFromItem((FromItem) where);
            return;
        }
        if (where.toString().indexOf("SELECT") > 0) {
            // 有子查询
            if (where instanceof BinaryExpression) {
                // 比较符号 , and , or , 等等
                BinaryExpression expression = (BinaryExpression) where;
                handleWhereSubSelect(expression.getLeftExpression());
                handleWhereSubSelect(expression.getRightExpression());
            } else if (where instanceof InExpression) {
                // in
                InExpression expression = (InExpression) where;
                Expression inExpression = expression.getRightExpression();
                if (inExpression instanceof Select) {
                    handleSelectBody(((Select) inExpression).getPlainSelect());
                }
            } else if (where instanceof ExistsExpression) {
                // exists
                ExistsExpression expression = (ExistsExpression) where;
                handleWhereSubSelect(expression.getRightExpression());
            } else if (where instanceof NotExpression) {
                // not exists
                NotExpression expression = (NotExpression) where;
                handleWhereSubSelect(expression.getExpression());
            } else if (where instanceof Expression) {
                Expression expression = (Expression) where;
                handleWhereSubSelect(expression);
            }
        }
    }

    /**
     * 处理子查询等
     */
    protected void processOtherFromItem(FromItem fromItem) {
        // 去除括号
//        while (fromItem instanceof ParenthesisFromItem) {
//            fromItem = ((ParenthesisFromItem) fromItem).getFromItem();
//        }
//
//        if (fromItem instanceof SubSelect) {
//            SubSelect subSelect = (SubSelect) fromItem;
//            if (subSelect.getSelectBody() != null) {
//                // INFO: Zhouwx: 2023/11/20 递归从select开始查找
//                handleSelectBody(subSelect.getSelectBody());
//            }
//        } else if (fromItem instanceof ValuesList) {
//            log.debug("Perform a subQuery, if you do not give us feedback");
//        } else if (fromItem instanceof LateralSubSelect) {
//            LateralSubSelect lateralSubSelect = (LateralSubSelect) fromItem;
//            if (lateralSubSelect.getPlainSelect() != null) {
//                Select subSelect = lateralSubSelect.getSelect();
//                if (subSelect.getPlainSelect() != null) {
//                    // INFO: Zhouwx: 2023/11/20 递归从select开始查找
//                    handleSelectBody(subSelect.getSelectBody());
//                }
//            }
//        }
    }

    protected void handleJoins(PlainSelect plainSelect) {
        List<Join> joins = plainSelect.getJoins();
        if (joins == null) {
            return;
        }

        for (Join join : joins) {
            // 获取表别名，并获取 表.del = 0
            FromItem rightItem = join.getRightItem();
            EqualsTo equalsTo = getEqualTo(rightItem);
            if(ObjectUtils.isNotNull(equalsTo)){
                // 获取on右边的表达式
                Expression onExpression = join.getOnExpression();
                // 给表达式增加 and 表.del = 0
                AndExpression andExpression = new AndExpression(onExpression, equalsTo);

                ArrayList<Expression> expressions = new ArrayList<>();
                expressions.add(andExpression);
                // 目前的这个表达式就是and后的表达式了，不用再增加原来的表达式，因为这个and表达式已经包含了原表达式所有的内容了
                join.setOnExpressions(expressions);
            }
        }
    }

    protected EqualsTo getEqualTo(FromItem fromItem) {
        Alias alias = fromItem.getAlias();
        String aliasName = "";
        boolean isIgnore = false;
        if (alias == null) {
            if (fromItem instanceof Table table) {
                aliasName = table.getName();
            }
        } else {
            aliasName = alias.getName();
        }

        if (fromItem instanceof Table table) {
            TableInfo tableInfo = TableInfoHelper.getTableInfo(table.getName());
            if(ObjectUtils.isNotNull(tableInfo)){
                isIgnore=tableInfo.isWithLogicDelete();
            }else {
                isIgnore=false;
            }
        }
        EqualsTo equalsTo = null;
        if (isIgnore) {
            equalsTo = new EqualsTo();
            Column leftColumn = new Column();
            leftColumn.setColumnName(aliasName + "." + logicDeleteField);
            equalsTo.setLeftExpression(leftColumn);
            equalsTo.setRightExpression(new StringValue(logicNotDeleteValue));
        }
        return equalsTo;
    }

    public String parserMulti(String sql, Object obj) {
        if (log.isDebugEnabled()) {
            log.debug("original SQL: " + sql);
        }
        try {
            // fixed github pull/295
            StringBuilder sb = new StringBuilder();
            Statements statements = JsqlParserGlobal.parseStatements(sql);
            int i = 0;
            for (Statement statement : statements) {
                if (i > 0) {
                    sb.append(StringPool.SEMICOLON);
                }
                sb.append(processParser(statement, i, sql, obj));
                i++;
            }
            return sb.toString();
        } catch (JSQLParserException e) {
            throw ExceptionUtils.mpe("Failed to process, Error SQL: %s", e.getCause(), sql);
        }
    }

    protected String processParser(Statement statement, int index, String sql, Object obj) {
        if (log.isDebugEnabled()) {
            log.debug("SQL to parse, SQL: " + sql);
        }
        if (statement instanceof Insert) {
//            this.processInsert((Insert) statement, index, sql, obj);
        } else if (statement instanceof Select) {
            this.processSelect((Select) statement, index, sql, obj);
        } else if (statement instanceof Update) {
//            this.processUpdate((Update) statement, index, sql, obj);
        } else if (statement instanceof Delete) {
//            this.processDelete((Delete) statement, index, sql, obj);
        }
        sql = statement.toString();
        if (log.isDebugEnabled()) {
            log.debug("parse the finished SQL: " + sql);
        }
        return sql;
    }

    /**
     * 新增
     */
    protected void processInsert(Insert insert, int index, String sql, Object obj) {
        throw new UnsupportedOperationException();
    }

    /**
     * 删除
     */
    protected void processDelete(Delete delete, int index, String sql, Object obj) {
        throw new UnsupportedOperationException();
    }

    /**
     * 更新
     */
    protected void processUpdate(Update update, int index, String sql, Object obj) {
        throw new UnsupportedOperationException();
    }

    /**
     * 查询
     */
    protected void processSelect(Select select, int index, String sql, Object obj) {
        throw new UnsupportedOperationException();
    }
}
